package chapter_19


class Publication(val title: String)
class Book(title: String) extends Publication(title)

object Library {

  val books: Set[Book] =
    Set(
      new Book("Programming in Scala"),
      new Book("walden")
    )

  def printBookList(info: Book => AnyRef): Unit = {
    for (book <- books) println(info(book))
  }
}


object LibraryApp {
  def getTitle(p: Publication): String = p.title

  def main(args: Array[String]): Unit = {
    /*
    printBookList方法枚举了它的书籍列表，并对每一本书调用传入的函数。它把返回的AnyRef
    类型的结果传递给println，由println对这个结果调用toString方法并打印。这个行为对于
    String及所有AnyRef子类的对象都有效，这也是函数结果类型的协变意义所在。
    现在请考虑一下传递给printBookList方法的函数参数类型。尽管printBookList的参数类型
    被声明为Book，而我们传递给它的getTitle带Publication参数，是Book的超类型。可以这样
    做的原因是printBookList的参数类型是Book，所以printBookList方法体将仅会传递Book对
    象给该函数。因为getTitle的参数类型为Publication，函数体将只能访问其参数p，是被声明为
    类Publication的成员。最后还由于任何声明在Publication内的方法在它的子类Book内也同样
    有效，因此所有环节都被大同了，这也就是函数参数类型逆变的意义所在。如下图：
    参数类型          Book => AnyRef          结果类型
      Book                  |                 AnyRef
        |                   |                   |
      Publication  Publication => String      String
    Publication => String是Book => AnyRef的子类型，所以上述代码得以编译通过。
    由于Function1的结果类型被定义为协变的，所以可以看到在图右侧显示的两个结果类型的继承
    关系在与中间的显示的两个函数的关系方向是一致的。相反，由于Function1的参数类型被定义
    为逆变的，所以左侧显示的两个参数类型的继承关系与函数关系的方向相反。
     */
    Library.printBookList(getTitle)
  }
}
